import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
from sklearn.manifold import Isomap, LocallyLinearEmbedding, SpectralEmbedding, TSNE
from sklearn.preprocessing import StandardScaler
import os
import time

# 设置中文显示
plt.rcParams["font.family"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC"]
plt.rcParams['axes.unicode_minus'] = False

# 加载MNIST数据集
def load_mnist(data_path=None):
    try:
        if data_path and os.path.exists(data_path):
            if data_path.endswith('.csv'):
                data = pd.read_csv(data_path)
                y = data.iloc[:, 0].values
                X = data.iloc[:, 1:].values
                print(f"成功从{data_path}加载数据，形状: {X.shape}")
                return X, y
            else:
                print("错误: 文件必须为CSV格式")
        # 生成模拟数据
        print("未找到数据集，生成模拟数据...")
        np.random.seed(42)
        X = np.random.rand(1000, 784) * 255
        y = np.random.randint(0, 10, 1000)
        return X, y
    except Exception as e:
        print(f"数据加载失败: {e}")
        return None, None

# 显示原始图像并记录日志
def plot_original_images(X, y, n=10, log_file=None):
    plt.figure(figsize=(12, 5))
    for i in range(n):
        plt.subplot(2, n//2, i+1)
        img = X[i].reshape(28, 28)
        plt.imshow(img, cmap='gray')
        plt.title(f'数字 {y[i]}')
        plt.axis('off')
    plt.tight_layout()
    plt.savefig('mnist_original_images.png')
    if log_file:
        with open(log_file, 'a') as f:
            f.write("[原始图像显示] 已保存10个样本图像到 mnist_original_images.png\n")
    plt.close()  # 关闭图像避免内存占用

# PCA特征分析并记录日志
def visualize_pca(X, log_file=None):
    scaler = StandardScaler()
    X_scaled = scaler.fit_transform(X)
    pca = PCA().fit(X_scaled)
    
    # 累积方差图
    plt.figure(figsize=(10, 6))
    plt.plot(range(1, len(pca.explained_variance_ratio_)+1), 
             np.cumsum(pca.explained_variance_ratio_), 'o-')
    plt.xlabel('主成分数量')
    plt.ylabel('累积方差解释率')
    plt.title('PCA累积方差解释率')
    plt.grid(True)
    plt.savefig('pca_variance_explained.png')
    if log_file:
        with open(log_file, 'a') as f:
            f.write("[PCA分析] 累积方差图已保存为 pca_variance_explained.png\n")
            f.write(f"[PCA数据] 前10个主成分方差解释率:\n")
            for i, ratio in enumerate(pca.explained_variance_ratio_[:10], 1):
                f.write(f"主成分 {i}: {ratio:.4f}\n")
    plt.close()
    
    # 主成分载荷图
    plt.figure(figsize=(12, 5))
    plt.subplot(1, 2, 1)
    plt.imshow(pca.components_[0].reshape(28, 28), cmap='viridis')
    plt.title('第一主成分载荷')
    plt.axis('off')
    plt.subplot(1, 2, 2)
    plt.imshow(pca.components_[1].reshape(28, 28), cmap='viridis')
    plt.title('第二主成分载荷')
    plt.axis('off')
    plt.tight_layout()
    plt.savefig('pca_components.png')
    if log_file:
        with open(log_file, 'a') as f:
            f.write("[PCA分析] 主成分载荷图已保存为 pca_components.png\n")
    plt.close()

# 降维可视化并记录日志
def visualize_dim_reduction(X, y, n_samples=500, log_file=None):
    idx = np.random.choice(len(X), n_samples, replace=False)
    X_subset, y_subset = X[idx], y[idx]
    scaler = StandardScaler()
    X_scaled = scaler.fit_transform(X_subset)
    
    methods = {
        'PCA': PCA(n_components=2),
        'Isomap': Isomap(n_components=2, n_neighbors=15),
        'LLE': LocallyLinearEmbedding(n_components=2, n_neighbors=15),
        'LE': SpectralEmbedding(n_components=2, n_neighbors=15),
        't-SNE': TSNE(n_components=2, random_state=42, perplexity=30)
    }
    
    plt.figure(figsize=(5*len(methods), 5))
    for i, (name, method) in enumerate(methods.items()):
        plt.subplot(1, len(methods), i+1)
        try:
            start_time = time.time()
            X_reduced = method.fit_transform(X_scaled)
            duration = time.time() - start_time
            scatter = plt.scatter(X_reduced[:, 0], X_reduced[:, 1], 
                                 c=y_subset, cmap='tab10', s=15, alpha=0.7)
            plt.title(f'{name} 降维 (耗时:{duration:.2f}s)')
            plt.axis('off')
            if log_file:
                with open(log_file, 'a') as f:
                    f.write(f"[{name}] 降维完成，耗时:{duration:.2f}秒，样本数:{n_samples}\n")
        except Exception as e:
            plt.text(0.5, 0.5, f"错误: {str(e)}", ha='center')
            if log_file:
                with open(log_file, 'a') as f:
                    f.write(f"[{name}] 降维失败: {str(e)}\n")
    plt.tight_layout()
    plt.savefig('mnist_dim_reduction.png')
    if log_file:
        with open(log_file, 'a') as f:
            f.write("[降维可视化] 结果已保存为 mnist_dim_reduction.png\n")
    plt.close()

# 主函数（含日志初始化）
def main():
    # 创建日志文件
    timestamp = time.strftime("%Y%m%d_%H%M%S")
    log_file = f"D:/exp4/mnist_dim_reduction_{timestamp}.log"
    with open(log_file, 'w') as f:
        f.write(f"[实验开始] MNIST降维可视化实验 - {timestamp}\n")
        f.write(f"{'='*50}\n")
    
    data_path = r"D:\exp4\train.csv"  # 数据集路径
    X, y = load_mnist(data_path)
    if X is None:
        with open(log_file, 'a') as f:
            f.write("[错误] 数据加载失败，实验终止\n")
        return
    
    # 记录数据集信息
    with open(log_file, 'a') as f:
        f.write(f"[数据集信息] 样本数:{len(X)}, 特征维度:{X.shape[1]}\n")
        f.write(f"[标签分布] {np.bincount(y)}\n\n")
    
    # 执行各步骤并记录日志
    plot_original_images(X, y, log_file=log_file)
    visualize_pca(X, log_file=log_file)
    visualize_dim_reduction(X, y, log_file=log_file)
    
    # 实验总结
    with open(log_file, 'a') as f:
        f.write(f"\n{'='*50}\n")
        f.write(f"[实验结束] 所有结果已保存，日志文件路径: {log_file}\n")
    print(f"实验完成！日志已保存至: {log_file}")

if __name__ == "__main__":
    main()